package com.gitee.wsl.io.channel

import com.gitee.wsl.io.channel.read.ByteReadChannel
import com.gitee.wsl.io.channel.read.ext.availableForRead


/**
 * Wrapper around data being serialized through calls.
 * Could be a reference to a string for a serialized object or to binary data.
 */
sealed class CallData<T> private constructor() {
    abstract val isBinary: Boolean

    /**
     * Read the serialized content of this object.
     * If this is not a string then throws [IllegalStateException].
     */
    abstract fun readSerialized(): T

    /**
     * Get the [ByteReadChannel] for the binary data held by this call..
     * If this is not binary data then throws [IllegalStateException].
     */
    abstract fun readBinary(): ByteReadChannel

    data class Binary<T>(private val value: ByteReadChannel) : CallData<T>() {
        override val isBinary: Boolean
            get() = true

        override fun readSerialized(): T =
            error("Cannot read serialization out of binary data.")

        override fun readBinary(): ByteReadChannel = value

        override fun toString(): String {
            return "binary(${(value as? ByteReadChannel)?.availableForRead})"
        }
    }

    data class Serialized<T>(private val value: T) : CallData<T>() {
        override val isBinary: Boolean
            get() = false

        override fun readSerialized(): T {
            return value
        }

        override fun readBinary(): ByteReadChannel {
            error("Cannot read binary data out of serialized content.")
        }

        override fun toString(): String {
            return value.toString()
        }
    }

    companion object {
        /**
         * Create a CallData holding content serialized in a string.
         */
        fun <T> create(str: T) = Serialized(str)

        fun createError(str: String) = Serialized("ERROR_" + str)

        /**
         * Create a CallData wrapping a [ByteReadChannel] for reading binary data.
         */
        fun <T> createBinary(binary: ByteReadChannel): CallData<T> = Binary(binary)
    }
}